﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using JSIF_WCFServer.Model;

namespace JSIF_WCFServer.Server
{    
    /// <summary>
    /// Serves the bookshelf using http.sys 
    /// </summary>
    public class BookshelfServer
    {
        /// <summary>
        /// Starts the server on the default port (18080)
        /// </summary>
        public BookshelfServer()
        {
            start();
        }
        /// <summary>
        /// Starts the server on the specified port
        /// </summary>
        /// <param name="port">the port to start the server on</param>
        public BookshelfServer(int port)
        {
            Port = port;
            start();
        }

        /// <summary>
        /// The port to start the server on
        /// </summary>
        /// <remarks>
        /// Changing this value will immedietly stop and restart the server on the update port
        /// </remarks>
        public int Port
        {
            get { return _port; }
            set
            {                
                lock (synclock)
                {
                    if (_port == value) { return; }
                    _port = value;
                    bool wasRunning = _serviceRunning;
                    if (wasRunning)
                    { stop(); }
                    _serviceInitialized = false;
                    if (wasRunning)
                    { start(); }
                }
            }
        }
        private int _port = 18080;

        private void init()
        {
            _host = new WebSiteHost();
            _host.AddEndpoint(new StaticFiles(), typeof(IStaticFiles), GetRawMsgBinding(),new Uri("http://localhost:" + Port + "")); 
            _host.AddEndpoint(new BookshelfReaderImpl(), typeof(IBookshelfReader), GetWebBinding(), new Uri("http://localhost:" + Port + "/bookshelf/read/"));
            _host.AddEndpoint(new BookshelfManagerImpl(), typeof(IBookshelfManager), GetWebBinding(), new Uri("http://localhost:" + Port + "/bookshelf/manage/"));
            _serviceInitialized = true;
        }

        static Binding GetWebBinding()
        {
            WebHttpBinding webBinding = new WebHttpBinding();
            webBinding.AllowCookies = true;
            return webBinding;
        }

        static Binding GetRawMsgBinding()
        {
            CustomBinding result = new CustomBinding(new WebHttpBinding());
            WebMessageEncodingBindingElement webMEBE = result.Elements.Find<WebMessageEncodingBindingElement>();
            webMEBE.ContentTypeMapper = new RawContentTypeMapper();
            return result;
        }

        private class RawContentTypeMapper : WebContentTypeMapper
        {
            public override WebContentFormat GetMessageFormatForContentType(string contentType)
            {
                return WebContentFormat.Raw;
            }
        }

        /// <summary>
        /// Starts the server
        /// </summary>
        public void start()
        {
            lock (synclock)
            {
                if (!_serviceInitialized)
                {
                    init();
                }
                if (!_serviceRunning)
                {
                    try
                    {
                        _host.Open();
                    }
                    catch (AddressAccessDeniedException)
                    {
                        throw new NeedElevatedPermissionException();
                    }

                    _serviceRunning = true;
                }
            }
        }

        /// <summary>
        /// Stops the server
        /// </summary>
        public void stop()
        {
            lock (synclock)
            {
                if (_serviceRunning)
                {
                    _host.Close();
                    _serviceRunning = false;
                }
            }
        }

        private Object synclock = new object();
        private bool _serviceRunning;
        private bool _serviceInitialized;

        WebSiteHost _host;
    }
}
